#include <Ogre.h>

#include "GameManager.h"
#include "GameState.h"

template<> GameManager* Ogre::Singleton<GameManager>::ms_Singleton = 0;

GameManager::GameManager()
{
  _root = NULL;
  _started = false;
  initSDL();
}

GameManager::~GameManager() { destroy(); }

void GameManager::destroy()
{
  while (!_states.empty())
  {
    _states.top()->exit();
    _states.pop();
  }
  
  _started = false;

  delete _inputMgr;
  delete _root;
}

void GameManager::start(GameState* state)
{
  // TODO: Montón de problemas cuando la pila de estados está vacía. Crear
  // estado «dummy» para que nunca lo esté.

  if (_started) destroy();
  _started = true;

  // Creación del objeto Ogre::Root.
  _root = new Ogre::Root();
  
  loadResources();

  if (not configure()) return;    

  // Creación del SceneManager.
  _sceneManager = _root->createSceneManager(Ogre::ST_GENERIC, "SceneManager");
  	
  // Creación de la cámara y el viewport.
  Ogre::Camera* cam = _sceneManager->createCamera("MainCamera");
  Ogre::Viewport* viewport = _renderWindow->addViewport(cam);
  double width = viewport->getActualWidth();
  double height = viewport->getActualHeight();
  cam->setAspectRatio(width / height);

  _inputMgr = new InputManager;
  _inputMgr->initialise(_renderWindow);

  // Registro como key y mouse listener...
  _inputMgr->addKeyListener(this, "GameManager");
  _inputMgr->addMouseListener(this, "GameManager");

  // El GameManager es un FrameListener.
  _root->addFrameListener(this);

  // Transición al estado inicial.
  changeState(state);

  // Bucle de rendering.
  _root->startRendering();
}

void GameManager::changeState(GameState* state)
{
  // Limpieza del estado actual.
  if (!_states.empty()) {
    // exit() sobre el último estado.
    _states.top()->exit();
    // Elimina el último estado.
    _states.pop();
  }

  // Transición al nuevo estado.
  _states.push(state);
  // enter() sobre el nuevo estado.
  _states.top()->enter();
}

void GameManager::pushState(GameState* state)
{
  // Pausa del estado actual.
  if (!_states.empty())
    _states.top()->pause();
  
  // Transición al nuevo estado.
  _states.push(state);
  // enter() sobre el nuevo estado.
  _states.top()->enter();
}

void GameManager::popState()
{
  // Limpieza del estado actual.
  if (!_states.empty()) {
    _states.top()->exit();
    _states.pop();
  }
  
  // Vuelta al estado anterior.
  if (!_states.empty())
    _states.top()->resume();
}

void GameManager::loadResources()
{
  Ogre::ConfigFile cf;
  cf.load("resources.cfg");
  
  Ogre::ConfigFile::SectionIterator sI = cf.getSectionIterator();
  Ogre::String sectionstr, typestr, datastr;

  while (sI.hasMoreElements())
  {
    sectionstr = sI.peekNextKey();
    Ogre::ConfigFile::SettingsMultiMap *settings = sI.getNext();
    Ogre::ConfigFile::SettingsMultiMap::iterator i;

    for (i = settings->begin(); i != settings->end(); ++i)
    {
      typestr = i->first;    datastr = i->second;
      Ogre::ResourceGroupManager::getSingleton().addResourceLocation
            (datastr, typestr, sectionstr);	
    }

  }
}

bool GameManager::configure()
{
  if (!_root->restoreConfig())
  {
    if (!_root->showConfigDialog())
      return false;
  }
  
  _renderWindow = _root->initialise(true, "Shadow City");
  
  Ogre::ResourceGroupManager::getSingleton().initialiseAllResourceGroups();
  
  return true;
}

GameManager* GameManager::getSingletonPtr() {
  return ms_Singleton; }

GameManager& GameManager::getSingleton()
{  
  assert(ms_Singleton);
  return *ms_Singleton;
}

// Las siguientes funciones miembro delegan
// el evento en el estado actual.
bool GameManager::frameStarted(const Ogre::FrameEvent& evt)
{
  _inputMgr->capture();
  return _states.top()->frameStarted(evt);
}

bool GameManager::frameEnded(const Ogre::FrameEvent& evt) {
  return _states.top()->frameEnded(evt); }

bool GameManager::keyPressed(const OIS::KeyEvent& e)
{
  _states.top()->keyPressed(e);
  return true;
}

bool GameManager::keyReleased(const OIS::KeyEvent& e)
{
  _states.top()->keyReleased(e);
  return true;
}

bool GameManager::mouseMoved(const OIS::MouseEvent &e)
{
  _states.top()->mouseMoved(e);
  return true;
}

bool GameManager::mousePressed(const OIS::MouseEvent &e, OIS::MouseButtonID id)
{
  _states.top()->mousePressed(e, id);
  return true;
}

bool GameManager::mouseReleased(const OIS::MouseEvent &e, OIS::MouseButtonID id)
{
  _states.top()->mouseReleased(e, id);
  return true;
}

bool GameManager::initSDL()
{
    // Inicializando SDL...
    if (SDL_Init(SDL_INIT_AUDIO) < 0)
        return false;
    // Llamar a  SDL_Quit al terminar.
    atexit(SDL_Quit);
 
    // Inicializando SDL mixer...
    if (Mix_OpenAudio(MIX_DEFAULT_FREQUENCY, MIX_DEFAULT_FORMAT,MIX_DEFAULT_CHANNELS, 4096) < 0)
      return false;
 
    // Llamar a Mix_CloseAudio al terminar.
    atexit(Mix_CloseAudio);
 
    return true;    
}
